/*
 * Copyright 2010 Michael Nitsche
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.mnit.basis.daten.struktur;

import java.util.Iterator;
import java.util.Vector;

import de.mnit.basis.daten.ArrayHilfe;
import de.mnit.basis.daten.WandleObjekt;
import de.mnit.basis.daten.struktur.liste.Liste;
import de.mnit.basis.daten.struktur.liste.S_Liste;
import de.mnit.basis.daten.struktur.ordnen.Sortieren;
import de.mnit.basis.fehler.Fehler;
import de.mnit.basis.sys.Ausgabe;

/**
 * @author Michael Nitsche
 */
public abstract class A_LM_Basis<T> implements Iterable<T>, S_LM_Basis<T> {

	protected Vector<T> daten;


	public A_LM_Basis() {
		this.daten=new Vector<T>();
	}


	// LESEN

	public int gLaenge() {
		return daten.size();
	}

	public int gBreite() {
		return 1;
	}

	public T gib(int pos) {
		Fehler.zahl.wenn_Gleich(pos, 0);
		Fehler.zahl.wenn_Ausserhalb(1, gLaenge(), pos); //TODO Ausgabe evtl. etwas optimieren! Seltsam wenn Min=1, Max=0, weil Liste leer!
		return daten.get(pos-1);
	}

	public T gibLetzten() {
		return gib(this.gLaenge());
	}

	public T gib(int spalte, int zeile) {
		Fehler.zahl.wenn_Nicht(1, spalte);
		return gib(zeile);
	}

	public S_Liste<T> gAuswahl(int... pos) {
		S_Liste<T> erg=Liste.neu();
		for(int p : pos)
			erg.plus(this.gib(p));
		return erg;
	}

	public Iterator<T> iterator() {
		return this.daten.iterator();
	}

	public boolean kennt(T o) {
		for(T t : daten)
			if(o.equals(t)) return true;
		return false;
	}

	public T[] zuBereich(Class<T> klasse) {
//		return this.daten.toArray(klasse);
		T[] ta=ArrayHilfe.nArray(klasse, gLaenge());
		for(int i=0; i<gLaenge();)
			ta[i]=gib(++i);
		return ta;
	}

	public Object[] zuBereich() {
		return this.daten.toArray();
	}

	public boolean istLeer() {
		return gLaenge()==0;
	}

	public String zuTextDebug() {
		return this.toString();
	}

	public String toString() {
		if(this.gLaenge()<=10) {
			boolean ok=true;
			for(T t : this)
				if(WandleObjekt.zuTextDebug(t).length()>15) {
					ok=false;
					break;
				}
			if(ok) {
				String erg="[";
				for(int i=1; i<=this.gLaenge(); i++)
					erg+= (i==1 ? "" : ",") + (WandleObjekt.zuTextDebug(this.gib(i)));
				erg+="]";
				return erg;
			}
		}

		StringBuffer sb=new StringBuffer();
		for(int i=1; i<=this.gLaenge(); i++) {
			sb.append(i==1 ? "[\n " : " ");
			sb.append(WandleObjekt.zuTextDebug(this.gib(i)));
			sb.append("\n");
			if(i==this.gLaenge()) sb.append("]");
		}
		return sb.toString();
	}

	public void ausgeben() {
		Ausgabe.debugVersetzt(1, this);
	}

	// ÄNDERN

	public abstract void plus(T objekt);
	public abstract void plus(int position, T objekt);

	public void sortieren(int... nach) {
		Sortieren.sortieren(this, nach);
	}

	public void plusAlle(T... objekte) {
		for(T objekt : objekte)
			this.plus(objekt);
	}

	public void plusAlle(Iterable<T> objekte) {
		for(T objekt : objekte)
			this.plus(objekt);
	}

	public void vertauschen(int pos_a, int pos_b) {
		Fehler.zahl.wenn_Ausserhalb(1, daten.size(), pos_a);
		Fehler.zahl.wenn_Ausserhalb(1, daten.size(), pos_b);
		pos_a--;
		pos_b--;

		if( pos_a == pos_b ) {
			Ausgabe.warnung("Vertauschen von "+(pos_a+1)+" und "+(pos_b+1)+" nicht nötig!");
			return;
		}

		T puffer=daten.get(pos_a);
		daten.set(pos_a, daten.get(pos_b));
		daten.set(pos_b, puffer);
	}

	public T minus(int pos) {
		Fehler.zahl.wenn_Ausserhalb(1, daten.size(), pos);
		return daten.remove(pos-1);
	}

	public T minusLetzten() {
		return minus(gLaenge());
	}

	public void setze(int pos, T neu) {
		pos--;
		Fehler.zahl.wenn_Ausserhalb(1, daten.size(), pos+1);

//		T puffer=daten.get(pos);
		daten.setElementAt(neu, pos);
//		return puffer;
	}

//	public void setze(int spalte, int pos, T neu) { //Rückgabewert wird nie genutzt!
//		Fehler.zahl.wenn_Nicht(1, spalte);
//		setze(pos, neu);
//	}

	public void leeren()   { this.daten=new Vector<T>(); }

	@SuppressWarnings("unchecked")
	public void sortiereSo(int[] so) {
		Fehler.zahl.wenn_Nicht(this.gLaenge(), so.length);
		Object[] puffer=new Object[this.gLaenge()];
		for(int i=0; i<puffer.length; i++)
			puffer[i]=this.daten.get(so[i]-1);
		for(int i=0; i<puffer.length; i++)
			this.daten.set(i, (T)puffer[i]);
	}

}
